package com.zhengming.juc;

import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.TransferQueue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;

public class T01_31_ThreadTest {

    private static Thread t1 = null, t2 = null;
    private static char[] aI = "1234567".toCharArray(), aC = "ABDEFG".toCharArray();

    public static void main(String[] args) {
        //t02_31_LockSupport_test();
        //sync_notify_wait_test();
        //lock_condition();
        test_LinkedTransferQueue();
    }

    private static void t02_31_LockSupport_test() {

        t1 = new Thread(() -> {
            for (char c: aI) {
                System.out.print(c);
                // 在这行调用了unpark(t2)，即使下面第三十行对t2 park()，t2也不会阻塞
                //这就是unpark在前保证了后序调用不会出问题
                LockSupport.unpark(t2); // 叫醒t2
                LockSupport.park(); // 阻塞t1
            }
        }, "t1");

        t2 = new Thread(() -> {
            for (char c: aC) {
                LockSupport.park();// 阻塞t2
                System.out.print(c);
                LockSupport.unpark(t1);// 叫醒t1
            }
        }, "t2");

        t1.start();
        t2.start();
    }

    private static void sync_notify_wait_test() {
        Object obj = new Object();
        new Thread(() -> {
            synchronized (obj) {
                for (char c: aI) {
                    System.out.print(c);
                    try {
                        obj.notify();
                        obj.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //for循环结束时唤醒当前线程，达到程序正常终止的目的
                obj.notify();
            }
        }).start();

        new Thread(() -> {
            synchronized (obj) {
                for (char c: aC) {
                    System.out.print(c);
                    try {
                        obj.notify();
                        obj.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                obj.notify();
            }
        }).start();
    }

    private static void lock_condition() {
        //生成可重入锁
        Lock lock = new ReentrantLock();
        //生成两种线程的队列，相当于存储线程的容器
        Condition conditionT1 = lock.newCondition(); // 线程队列
        Condition conditionT2 = lock.newCondition();

        new Thread(() -> {
            //可重入锁，对当前线程加锁，可理解为synchronized
            lock.lock();
            try {
                for (char c: aI) {
                    System.out.print(c);
                    //唤醒T2线程队列中的某个线程
                    conditionT2.signal();
                    //当前线程调用哪个线程队列实例的await()，就进入那个线程队列并等待
                    //必须调用lock.lock();，才能进入队列
                    conditionT1.await();
                }
                conditionT2.signal();
            } catch (InterruptedException e) {
                lock.unlock();
                e.printStackTrace();
            }
        }, "t1").start();

        new Thread(() -> {
            try {
                lock.lock();
                for (char c: aC) {
                    System.out.print(c);
                    //唤醒T1线程队列中的某个线程
                    //当前线程进入conditionT2队列等待
                    conditionT1.signal();
                    conditionT2.await();
                }
                conditionT1.signal();
            } catch (InterruptedException e) {
                lock.unlock();
                e.printStackTrace();
            }
        }, "t2").start();
    }

    private static void test_LinkedTransferQueue() {
        TransferQueue<Character> queue = new LinkedTransferQueue<>();

        new Thread(() ->{
            try {
                for (char c : aI) {
                    /**
                     * 将当前的字符用transfer塞进queue中，等待其他线程将这个字符拿出
                     * 之后当前线程执行完transfer之后将堵塞不再进行下去。
                     *      此时线程2将会在执行take()，将刚刚塞入的字符拿出来，因为take()的方法也是堵塞的方法，
                     *      所以这两个线程一定是take等到transfer放进去，take在自动执行拿出来,才继续往下走
                     *      所以在线程2的take方法拿出来的时候线程2也会继续走下去执行transfer(),将线程2的字符放进去
                     *      之后等待线程1再take出来
                     *  即整个流程不管线程启动的顺序有多不一定，线程二一定会等待线程一将字符使用transfer放进去，线程二阻塞的take才会执行。
                     *
                     *  相当于两个字符串交换的输出，而不是在原有的线程中输出了。
                     *      1A2B3D4E5F6G
                     */
                    queue.transfer(c);
                    System.out.print(queue.take());
                }
            } catch (InterruptedException e){
                e.printStackTrace();
            }
        }, "t1").start();

        new Thread(() ->{
            try {
                for (char c : aC) {
                    System.out.print(queue.take());
                    queue.transfer(c);
                }
            } catch (InterruptedException e){
                e.printStackTrace();
            }
        }, "t2").start();
    }
}
